<template>
  <div class="draw_graph_warp">
    <div v-show="xmlData.length === 0" class="no_data">暂无数据</div>
    <div id="draw_graph_container"></div>
    <div id="draw_graph_button_warp" v-show="showTool"></div>
  </div>
</template>
<script>
/*
		by rao
		props 
				stateData:
				[
					{
						equipId: 'PA01-A02',  //用来匹配xml的equipId
						color: 'red',	//告警的颜色
						isWarning: false,//是否显示告警图标
					}
				]
				xmlData:'' //默认空字符串 drawio导出的xml文件
				warningImg:'' //告警图标样式
		emit 	
				@leftClick   左键点击
				@rightClick  右键点击
				@loadGetNodes   xml文档加载，视图渲染完成时触发,获取所有节点
	*/

import graph from './utils/config.js';
import drawDecode from './utils/drawDecode.js';
import defaultXml from './utils/default.js';

export default {
  name: 'graph-view',
  props: {
    stateData: {
      type: Array,
      default: () => [],
    },
    xmlData: {
      type: String,
      default: '',
    },
    warningImg: {
      type: String,
      default:
        '',
    },
    showTool: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      graph: null,
      nodesDefaultStyles: [], // xml图默认的样式
    };
  },
  watch: {
    xmlData(newVal, oldVal) {
      if (newVal) {
        this.setView();
      }
    },
    stateData: {
      handler(newVal, oldVal) {
        if (newVal) {
          this.setStateData();
        }
      },
      deep: true,
    },
  },
  mounted() {
    this.initGraph();
    // 注册工具栏
    this.setTool();
    // 注册事件
    this.setEvent();
  },
  created() {},
  methods: {
    // 初始化graph
    initGraph() {
      if (this.graph) {
        // 销毁重置容器视图
        this.graph.destroy();
      }
      if (!mxClient.isBrowserSupported()) {
        mxUtils.error('Browser is not supported!', 200, false);
      } else {
        // 初始化graph
        const container = document.getElementById('draw_graph_container');
        this.graph = new Graph(container);
        // 设置风格样式themes default.xml文件
        const themsXml = mxUtils.parseXml(defaultXml);
        new mxCodec().decode(
          themsXml.documentElement,
          this.graph.getStylesheet()
        );
        // 设置一些禁止拖动
        this.graph.setEnabled(false);
        this.graph.allowAutoPanning = false;
        // 高亮效果
        new mxCellTracker(this.graph, 'black');
        // 设置鼠标拖动画布
        this.graph.panningHandler.useLeftButtonForPanning = true;
        this.graph.panningHandler.ignoreCell = true;
        this.graph.container.style.cursor = 'move';
        this.graph.setPanning(true);
        this.graph.setTooltips(false); // 取消默认的tooltip

        // 初始化执行一次视图渲染
        this.setView();
        this.setStateData();
      }
    },
    // 设置事件
    setEvent() {
      let time = null;
      // 点击事件
      this.graph.click = (e) => {
        clearTimeout(time);
        time = setTimeout(() => {
          document.getElementById('draw_graph_container').oncontextmenu =
            function (e) {
              // 取消默认的浏览器自带右键
              return false;
            };
          if (e.state === undefined) {
            return false;
          }
          // 左键点击
          if (e.evt.button === 0) {
            this.$emit('leftClick', e);
          }
          // 右键点击
          if (e.evt.button === 2) {
            this.$emit('rightClick', e);
          }
        }, 300);
      };
      // 双击事件
      this.graph.dblClick = (evt, cell) => {
        clearTimeout(time);
        this.$emit('dblClick', evt, cell);
      };
    },
    // 工具栏button
    setTool() {
      document.getElementById('draw_graph_button_warp').appendChild(
        mxUtils.button('+', () => {
          this.graph.zoomIn();
        })
      );
      document.getElementById('draw_graph_button_warp').appendChild(
        mxUtils.button('-', () => {
          this.graph.zoomOut();
        })
      );
      document.getElementById('draw_graph_button_warp').appendChild(
        mxUtils.button('打印', (evt) => {
          const preview = new mxPrintPreview(this.graph, 1);
          preview.autoOrigin = false;
          preview.printOverlays = true;
          preview.open();
        })
      );
    },
    // 更新视图
    setView() {
      if (this.xmlData) {
        const xmlDoc = mxUtils.parseXml(drawDecode(this.xmlData));
        const codec = new mxCodec(xmlDoc);
        codec.decode(xmlDoc.documentElement, this.graph.getModel());
        this.graph.center(true, true, 0.5, 0.5);
        const sc = this.graph.getView().getScale();
        const nodes = this.graph.getDefaultParent().children;
        // 视图更新完成，将节点emit分发出去
        this.$emit('loadGetNodes', nodes);
        // 保存一个默认的样式，用于后面重置样式
        this.getNodesDefaultStyles(nodes);
        // 先清空现在的状态,将添加的样式全清空
        this.resetGraph(nodes);
      }
    },
    // 根据容器尺寸放缩
    fitWindow() {
      const bounds = this.graph.getGraphBounds();
      const t = this.graph.view.translate;
      const s = this.graph.view.scale;
      bounds.x = bounds.x / s - t.x;
      bounds.y = bounds.y / s - t.y;
      bounds.width /= s;
      bounds.height /= s;
      this.graph.fitWindow(bounds);
      this.graph.fit();
    },
    // 更新状态显示
    setStateData() {
      // 存在视图数据
      if (!this.xmlData) {
        return;
      }
      const nodes = this.graph.getDefaultParent().children;
      // 在每次重新更新状态数据前，先清空重置样式
      if (nodes) {
        this.resetGraph(nodes);
      }
      // 根据得到的状态来绘制样式
      // 获取初始化界面每个节点
      if (nodes && this.stateData.length > 0) {
        this.stateData.map((state) => {
          nodes.map((cellItem) => {
            const equipId = cellItem.value
              ? cellItem.value.getAttribute('equipId')
              : null;
            // 设备通过equipId来匹配
            if (state.equipId === equipId) {
              const color = state.color ? state.color : null;
              const isWarning = state.isWarning ? state.isWarning : false;
              const effectType = state.effectType ? state.effectType : null;
              this.addOverlay(cellItem.id, color, isWarning, effectType);
            }
          });
        });
      }
    },
    // 创建告警信息
    createOverlay(image) {
      const overlay = new mxCellOverlay(image);
      overlay.cursor = null;
      return overlay;
    },
    // 添加告警状态
    addOverlay(id, color, isWarning, effectType) {
      // 根据ID获取cell
      const cell = this.graph.getModel().getCell(id);
      /*
					修改有报警物体的样式
					STYLE_STROKEWIDTH  边框宽度
					STYLE_STROKECOLOR  边框颜色线条颜色
					STYLE_FONTCOLOR    文字颜色
					STYLE_IMAGE_BORDER 图片边框
				*/
      if (color) {
        this.graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, 2, [cell]);
        this.graph.setCellStyles(mxConstants.STYLE_STROKECOLOR, color, [cell]);
        this.graph.setCellStyles(mxConstants.STYLE_FONTCOLOR, color, [cell]);
        this.graph.setCellStyles(mxConstants.STYLE_IMAGE_BORDER, color, [cell]);
      }
      /*
					添加告警图标 
					graph源码需要获取图片高宽 => new Image() 
				*/
      if (isWarning) {
        const img = new Image();
        img.src = this.warningImg;
        img.onload = () => {
          this.graph.addCellOverlay(cell, this.createOverlay(img));
        };
      }
      /*
					涟漪效果
				*/
      if (effectType) {
        const {x} = this.graph.view.getState(cell);
        const {y} = this.graph.view.getState(cell);
        const w = this.graph.view.getState(cell).width;
        const h = this.graph.view.getState(cell).height;
        this.graph.view
          .getState(cell)
          .shape.node.appendChild(this.addHtml(x, y, w, h, color));
      }
    },
    // 删除告警闪烁图标
    delOverlay(id) {
      // 获取ID单元
      const cell = this.graph.getModel().getCell(id);
      // 移除告警
      this.graph.removeCellOverlays(cell);
    },
    /*
				保存默认样式
			*/
    getNodesDefaultStyles(nodes) {
      const obj = {};
      nodes.map((item) => {
        const obj = {
          id: item.id,
          style: item.style,
        };
        this.nodesDefaultStyles.push(obj);
      });
    },
    /*
				重置为默认样式
				nodesDefaultStyles=>初始化界面后，保存的默认样式
			*/
    resetGraph(nodes) {
      if (this.nodesDefaultStyles.length) {
        this.nodesDefaultStyles.map((item) => {
          nodes.map((mxCell) => {
            if (item.id === mxCell.id) {
              mxCell.setStyle(item.style);
              this.delOverlay(mxCell.id);
              this.removeHtml(mxCell);
            }
          });
        });
      }
      this.graph.refresh();
    },
    /*
				是否是base64的图片
			*/
    isBase64(data) {
      const reg =
        /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i;
      return !!reg.test(data);
    },
    /*
				添加涟漪特效
			*/
    addHtml(x, y, w, h, color) {
      //				let effectRange = w > h ? h : w;
      const html = document.createElementNS(
        'http://www.w3.org/2000/svg',
        'foreignObject'
      );
      html.setAttribute('x', x);
      html.setAttribute('y', y - w / 2 + h / 2);
      html.setAttribute('width', w);
      html.setAttribute('height', w);
      html.innerHTML = `<body xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%; background: transparent;"><div class="draw_graph_effect" style='background:${color}'><div class="draw_graph_effect2"></div></div></body>`;
      return html;
    },
    /*
				移出特效
			*/
    removeHtml(cell) {
      if (
        this.graph.view
          .getState(cell)
          .shape.node.getElementsByTagName('foreignObject').length
      ) {
        this.graph.view
          .getState(cell)
          .shape.node.getElementsByTagName('foreignObject')[0]
          .remove();
      }
    },
  },
};
</script>

<style lang="less">
.draw_graph_warp {
  position: relative;
  width: 100%;
  height: 100%;
}

#draw_graph_container {
  width: 100%;
  height: 100%;
  min-width: 100px;
  min-height: 100px;
  border: 1px solid #e8e8e8;
  touch-action: none;
  cursor: default;
  overflow: hidden;
}

.no_data {
  position: absolute;
  width: 100%;
  height: 100%;
  border: 1px solid #e8e8e8;
  background: #fff;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
}

#draw_graph_button_warp {
  position: absolute;
  width: 48px;
  top: 0;
  left: 0;
  z-index: 11;
  display: flex;
  flex-direction: column;
  border: 1px solid #d9d9d9;
  border-top: 0;

  button {
    color: rgba(0, 0, 0, 0.65);
    background-color: #fff;
    border: 0;
    border-top: 1px solid #d9d9d9;
    font-size: 14px;
    cursor: pointer;
    height: 30px;
  }
}

.draw_graph_effect {
  margin: auto;
  line-height: 1;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  display: inline-block;
  animation: draw_graph_effect 1s ease-out infinite;
}
@keyframes draw_graph_effect {
  0% {
    transform: scale(0.4);
    opacity: 0.5;
  }

  30% {
    opacity: 0.3;
  }

  100% {
    transform: scale(0.8);
    opacity: 0;
  }
}
</style>
